﻿using System;
using System.Collections;
using System.Linq;

namespace System.Collections.Generic
{
    public static class DictionaryExtension
    {
        #region Sort 排序
        /// <summary>
        /// Sorts the specified dictionary.
        /// </summary>
        /// <typeparam name="TKey">The type of the key.</typeparam>
        /// <typeparam name="TValue">The type of the value.</typeparam>
        /// <param name="dictionary">The dictionary.</param>
        /// <returns></returns>
        public static IDictionary<TKey, TValue> Sort<TKey, TValue>(this IDictionary<TKey, TValue> dictionary)
        {
            if (dictionary == null)
                throw new ArgumentNullException("dictionary");

            return new SortedDictionary<TKey, TValue>(dictionary);
        }

        /// <summary>
        /// Sorts the specified dictionary.
        /// </summary>
        /// <typeparam name="TKey">The type of the key.</typeparam>
        /// <typeparam name="TValue">The type of the value.</typeparam>
        /// <param name="dictionary">The dictionary to be sorted.</param>
        /// <param name="comparer">The comparer used to sort dictionary.</param>
        /// <returns></returns>
        public static IDictionary<TKey, TValue> Sort<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, IComparer<TKey> comparer)
        {
            if (dictionary == null)
                throw new ArgumentNullException("dictionary");
            if (comparer == null)
                throw new ArgumentNullException("comparer");

            return new SortedDictionary<TKey, TValue>(dictionary, comparer);
        }

        /// <summary>
        /// Sorts the dictionary by value.
        /// </summary>
        /// <typeparam name="TKey">The type of the key.</typeparam>
        /// <typeparam name="TValue">The type of the value.</typeparam>
        /// <param name="dictionary">The dictionary.</param>
        /// <returns></returns>
        public static IDictionary<TKey, TValue> SortByValue<TKey, TValue>(this IDictionary<TKey, TValue> dictionary)
        {
            return (new SortedDictionary<TKey, TValue>(dictionary)).OrderBy(kvp => kvp.Value).ToDictionary(item => item.Key, item => item.Value);
        }
        #endregion //SortDirctionary

        #region Sort 排序

        /// <summary>
        /// 对键值对进行颠倒，把键当成值，值当成键
        /// Inverts the specified dictionary. (Creates a new dictionary with the values as key, and key as values)
        /// </summary>
        /// <typeparam name="TKey">The type of the key.</typeparam>
        /// <typeparam name="TValue">The type of the value.</typeparam>
        /// <param name="dictionary">The dictionary.</param>
        /// <returns></returns>
        public static IDictionary<TValue, TKey> ToInvert<TKey, TValue>(this IDictionary<TKey, TValue> dictionary)
        {
            if (dictionary == null)
                throw new ArgumentNullException("dictionary");
            return dictionary.ToDictionary(pair => pair.Value, pair => pair.Key);
        }

        /// <summary>
        /// Creates a (non-generic) Hashtable from the Dictionary.
        /// </summary>
        /// <typeparam name="TKey">The type of the key.</typeparam>
        /// <typeparam name="TValue">The type of the value.</typeparam>
        /// <param name="dictionary">The dictionary.</param>
        /// <returns></returns>
        public static Hashtable ToHashTable<TKey, TValue>(this IDictionary<TKey, TValue> dictionary)
        {
            var table = new Hashtable();

            foreach (var item in dictionary)
                table.Add(item.Key, item.Value);

            return table;
        }

        #endregion //Convert 转换

        #region Add 插入
        /// <summary>
        /// 插入
        /// </summary>
        /// <typeparam name="TKey"></typeparam>
        /// <typeparam name="TValue"></typeparam>
        /// <param name="source"></param>
        /// <param name="dictionary"></param>
        public static void AddRange<TKey, TValue>(this IDictionary<TKey, TValue> source, IDictionary<TKey, TValue> dictionary)
        {
            foreach (var item in dictionary)
            {
                source.Add(item.Key, item.Value);
            }
        }

        /// <summary>
        /// 获取或添加键值对
        /// </summary>
        /// <typeparam name="TKey"></typeparam>
        /// <typeparam name="TValue"></typeparam>
        /// <param name="dictionary"></param>
        /// <param name="key"></param>
        /// <param name="createValueDelegate"></param>
        /// <returns></returns>
        public static TValue GetOrAdd<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, Func<TValue> createValueDelegate)
        {
            TValue result;
            if (!dictionary.TryGetValue(key, out result))
            {
                dictionary[key] = (result = createValueDelegate());
            }
            return result;
        }

        /// <summary>
        /// 添加或更新一个值
        /// </summary>
        /// <typeparam name="TKey"></typeparam>
        /// <typeparam name="TValue"></typeparam>
        /// <param name="dic"></param>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public static void AddOrUpdate<TKey, TValue>(this IDictionary<TKey, TValue> dic, TKey key, TValue value)
        {
            if (dic.ContainsKey(key))
                dic[key] = value;
            else dic.Add(key, value);
        }
        #endregion //Add 插入

        #region Get 获取
        /// <summary>
        /// Returns the value of the first entry found with one of the <paramref name="keys"/> received.
        /// <para>Returns <paramref name="defaultValue"/> if none of the keys exists in this collection </para>
        /// </summary>
        /// <param name="defaultValue">Default value if none of the keys </param>
        /// <param name="keys"> keys to search for (in order) </param>
        public static TValue GetFirstValue<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TValue defaultValue, params TKey[] keys)
        {
            foreach (var key in keys)
            {
                if (dictionary.ContainsKey(key))
                    return dictionary[key];
            }
            return defaultValue;
        }

        /// <summary>
        /// 获取键值对,如果没有匹配键值，则返回默认
        /// </summary>
        /// <typeparam name="TKey"></typeparam>
        /// <typeparam name="TValue"></typeparam>
        /// <param name="dictionary"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        public static TValue GetOrDefault<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key)
        {
            TValue result;
            dictionary.TryGetValue(key, out result);
            return result;
        }

       

        /// <summary>
        /// 尝试从字典中获得键值,无匹配键则返回默认值
        /// </summary>
        /// <typeparam name="Tkey">字典的键类型</typeparam>
        /// <typeparam name="TValue">字典的值类型</typeparam>
        /// <param name="dic">字典对象</param>
        /// <param name="key">检索的键</param>
        /// <returns>返回要查找的对象</returns>
        public static TValue GetValue<Tkey, TValue>(this Dictionary<Tkey, TValue> dic, Tkey key)
        {
            return dic.GetValue(key, default(TValue));
        }

        /// <summary>
        /// 尝试从字典中获得值, 如果不包含则放入一个由指定函数返回的初始值
        /// </summary>
        /// <typeparam name="Tkey">字典的键类型</typeparam>
        /// <typeparam name="TValue">字典的值类型</typeparam>
        /// <param name="dic">字典对象</param>
        /// <param name="key">检索的键</param>
        /// <returns>返回要查找的对象</returns>
        public static TValue GetValue<Tkey, TValue>(this Dictionary<Tkey, TValue> dic, Tkey key, Func<Tkey, TValue> initialValueFunc)
        {
            if (dic.ContainsKey(key))
                return dic[key];

            var value = default(TValue);
            if (initialValueFunc == null)
                return default(TValue);

            lock (dic)
            {
                if (!dic.ContainsKey(key))
                    dic.Add(key, value = initialValueFunc(key));
            }

            return dic[key];
        }

        /// <summary>
        /// 尝试从字典中获得值
        /// </summary>
        /// <typeparam name="Tkey">字典的键类型</typeparam>
        /// <typeparam name="TValue">字典的值类型</typeparam>
        /// <param name="dic">字典对象</param>
        /// <param name="key">检索的键</param>
        /// <param name="defaultValue">没有找到时返回的默认值</param>
        /// <returns>返回要查找的对象</returns>
        public static TValue GetValue<Tkey, TValue>(this Dictionary<Tkey, TValue> dic, Tkey key, TValue defaultValue)
        {
            TValue value = default(TValue);
            if (!dic.TryGetValue(key, out value))
                value = defaultValue;

            return value;
        }

        /// <summary>
        /// 查找一个有序字典中指定的键的索引
        /// </summary>
        /// <typeparam name="TKey">字典的键类型</typeparam>
        /// <typeparam name="TValue">字典的值类型</typeparam>
        /// <param name="dic">字典</param>
        /// <param name="key">要查找的键</param>
        /// <returns>返回相应的索引；如果没找到，则返回 -1</returns>
        public static int GetIndex<TKey, TValue>(this SortedDictionary<TKey, TValue> dic, TKey key)
        {
            if (dic == null)
                return -1;

            var idx = 0;
            foreach (var keyidx in dic.Keys)
            {
                if (keyidx.Equals(key))
                    return idx;
                idx++;
            }

            return -1;
        }


        /// <summary>
        /// Returns the value associated with the specified key, or throw the specified exception if no element is found.
        /// </summary>
        /// <typeparam name="TKey">The key data type</typeparam>
        /// <typeparam name="TValue">The value data type</typeparam>
        /// <param name="source">The source dictionary.</param>
        /// <param name="key">The key of interest.</param>
        /// <param name="exception">The exception to throw if the key is not found.</param>
        /// <returns>The value associated with the specified key if the key is found, the specified exception is thrown if the key is not found</returns>
        public static TValue GetOrThrow<TKey, TValue>(this IDictionary<TKey, TValue> source, TKey key, Exception exception)
        {
            TValue value;
            if (source.TryGetValue(key, out value))
            {
                return value;
            }

            throw exception;
        }
        #endregion //Get 获取

        #region Remove 移除

        /// <summary>
        /// 尝试移除一个键
        /// </summary>
        /// <typeparam name="TKey"></typeparam>
        /// <typeparam name="TValue"></typeparam>
        /// <param name="dic"></param>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public static bool TryRemove<TKey, TValue>(this IDictionary<TKey, TValue> dic, TKey key, out TValue value)
        {
            value = default(TValue);
            if (dic.ContainsKey(key))
            {
                lock (dic)
                {
                    if (dic.ContainsKey(key))
                    {
                        value = dic[key];
                        dic.Remove(key);

                        return true;
                    }
                }
            }
            return false;
        }
        #endregion //Remove 移除

      
    }
}